﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Noris.Tools.TraceVisualiser.Interface;
using Noris.Tools.TraceVisualiser.Support;
using Noris.Tools.TraceVisualiser.Components;
using Noris.Tools.TraceVisualiser.TraceData;
using Noris.Tools.TraceVisualiser.Green;

namespace Noris.Tools.TraceVisualiser.Green.DbBlocking
{
    #region TraceReader : reader pro soubory typu Log blokování na aplikačním serveru
    /// <summary>
    /// TraceReader : reader pro soubory typu Log blokování na aplikačním serveru
    /// </summary>
    public class TraceReader : ITraceReader
    {
		#region IPlugin Members
		/// <summary>
		/// Příznak, že Plugin je aktivní ve standardním běhu (tj. při běžném užiavtelském spuštění).
		/// Plně funkční pluginy tedy vrací true.
		/// Pluginy, které jsou ve vývoji, vrací false, takové jsou zobrazeny / spouštěny pouze v aplikaci, která běží v režimu Debug.
		/// </summary>
		bool IPlugin.Active { get { return true; } }
		#endregion
		#region ITraceReader, IDisposable Members
        /// <summary>
        /// Zde reader vrací výraz, který se použije v dialogu při výběru vstupních trace soubor.
        /// Různé readery mají různé vstupní soubory s různou příponou, a my nechceme žádný reader opomenout.
        /// Reader může umět zpracovávat více různých koncovek, proto se používá string[], každá položka obsahuje jeden filtr.
        /// Každý jednotlivý filtr je ve tvaru: "Textový popisek|filtr", například: "Aplikační trace Green (*.csv)|*csv".
        /// </summary>
        string[] ITraceReader.FileOpenFilters
        {
            get
            {
                return new string[]
                {
                    "Helios Green blocking trace files (*.csv)|*.csv"
                };
            }
        }
        /// <summary>
        /// Metoda má za úkol podívat se na název souboru,
        /// a vyjádřit se k tomu, zda se bude chtít podívat i dovnitř souboru (tím, že nastaví args.CheckContent = true).
        /// Metoda by měla (pokud se tento TraceReader dokáže s názvem souboru vypořádat) rozebrat název souboru na jméno serveru, a oddělit i pořadové číslo z názvu.
        /// Metoda absolutně nemá za úkol otevírat soubor, pracuje jen s názvem.
        /// Pokud se metoda bude chtít podívat do souboru, nastaví args.CheckContent = true, a za chvíli se vyvolá další metoda: this.CheckContent().
        /// </summary>
        /// <param name="args">Data o souboru (název a výsledky této metody)</param>
        /// <returns></returns>
        void ITraceReader.CheckFileName(TraceReaderCheckNameArgs args)
        {
            // Nechci se dívat dovnitř (možná časem změním názor):
            args.CheckContent = false;

            // Přípona má být ".csv"
            System.IO.FileInfo fileInfo = args.FileInfo;
            if (!String.Equals(fileInfo.Extension, ".csv", StringComparison.OrdinalIgnoreCase))
                return;

            // Jméno má začínat "Trace" a obsahovat _blocking_, anebo podle nového formátu (funkce pro Export logu blokování) musí začínat "BlockingLog_":
            string name = System.IO.Path.GetFileNameWithoutExtension(args.FileInfo.Name);
            FileFormat format = FileFormat.None;
            DateTime date;
            string dbName = null;
            if (name.StartsWith("trace", StringComparison.OrdinalIgnoreCase) && name.IndexOf("_blocking_", StringComparison.OrdinalIgnoreCase) > 0)
            {   // Minulý formát názvu, pro soubor který se generuje automaticky:
                // má mít určitou strukturu:
                string fileRoot, nameDate;
                if (!ParseFileNameV1(args.FileInfo.Name, out fileRoot, out nameDate, out dbName))
                    return;

                date = DetectNameDate(nameDate);
                if (date == DateTime.MinValue)
                    return;

                format = FileFormat.OnlineSave;
                args.UserData.Add("DbName", dbName);          // Název databáze se dostane do všech návazných argumentů (do metod this.CheckContent() i this.
                args.UserData.Add("Format", format);
                args.UserData.Add("Date", date);
            }
            else if (name.StartsWith("BlockingLog_", StringComparison.OrdinalIgnoreCase))
            {   // Formát názvu vytvořeného funkcí nad dynamickým oknem:
                // má mít určitou strukturu (BlockingLog_2013.01.01_00.00__2013.02.11_00.00)
                DateTime? dateBegin, dateEnd;
                if (!ParseFileNameV2(args.FileInfo.Name, out dateBegin, out dateEnd, out dbName))
                    return;
                
                if (!dateBegin.HasValue)
                    return;
                date = dateBegin.Value;

                format = FileFormat.ExportFunction;
                args.UserData.Add("Format", format);
                args.UserData.Add("DateBegin", dateBegin.Value);
                args.UserData.Add("DateEnd", dateEnd.Value);
            }
            else
                return;


            // Vypadá to nadějně, určíme název serveru:
            // Server se zde má na mysli databázový, a ne aplikační. To proto, že (ačkoliv data generuje aplikační server do svého adresáře), jde o data SQL serveru.
            // Název serveru tedy bude dán fixní = "SqlBlocking", bez přidání jména databáze a data (jedna každá databáze bude tvořit oddělený proces v rámci serveru).
            // Velký význam to má při přidávání eventů, kdy máme jejich ID = číslo nonsubjektu, které je unikátní v rámci jedné databáze.
            // Nepředpokládám, že bych v rámci jednoho trace projektu mohl mít v různých adresářích soubory trace_blocking, které by názvem vedly ke stejnému serveru, a přitom byly obsahově jiné.
            // V rámci jednoho serveru existují "procesy", proces v tomto druhu trace bude mít identifikátor = název databáze (uvedený v konkrétním řádku trace).
            // V rámci procesu existují eventy: zdejší trace obsahuje eventy, jejichž identifikátor je ID (=číslo nonsubjektu).
            // Tak je zajištěno, že ze všech logů blokování se budou data vkládat do jednoho "serveru", kde budou odděleny procesy = databáze, a v každé databázi bude unikátní event=ID.
            args.ServerName = NorisBlockingConstants.ServerName;

            // Do argumentu vložím datum souboru odvozené z jeho záhlaví a název databáze odtamtéž:
            args.TraceDate = date;

            // Šlo by to, podíváme se dovnitř:
            args.CheckContent = true;
        }
        /// <summary>
        /// Metoda je volaná tehdy, když tento reader se přihlásil ke čtení záhlaví souboru (v metodě CheckFileName, nastavil args.CheckContent = true).
        /// Tato metoda nemá za úkol číst data, ale podívat se dovnitř, a usoudit, zda my chceme tento soubor zpracovávat.
        /// Soubor je otevřen, a je k němu přístup přes argument
        /// </summary>
        /// <param name="args"></param>
        void ITraceReader.CheckContent(TraceReaderCheckContentArgs args)
        {
            // Nechci soubor zpracovávat (možná časem změním názor):
            args.Priority = 0M;

            // Ověřím titulkový řádek:
            // Zatím umím jen tento řádek:
            string expected = "id;blocked;blocking;dbname;objname_indname;blcktime;type;mode;status;loginame;type2;mode2;status2;loginame2;blcktime2;currenttime;rsc_bin;lasttime;updating;total_blcktime;resource;indid;blocked_cmd;blocking_cmd;fnc;fnc2;fnc_name;fnc_name2;started;started2;ended;ended2;stav;stav2;aitems;aitems2;note;note2;auditlog;auditlog2;noris_user;noris_user2;totally_blocked;program_name;program_name2;session_id;session_id2;user_id;user_id2";
			string title = args.ReadFirstLine();
			if (title.Length != expected.Length) return;
            if (String.Compare(title, expected, StringComparison.CurrentCultureIgnoreCase) != 0) return;

            // Najdu začátek a konec dat v souboru:
            string dbName = null;
            TimeRange contentTime = TimeRange.Empty;
            string line = args.FileStreamReader.ReadLine();
            contentTime.Begin = _GetTimeRangeFromLine(line, ref dbName).Begin;

            // Najdu poslední řádek, který obsahuje alespoň 47 prvků oddělených středníkem:
            line = args.ReadLastLine(";", 47);
            contentTime.End = _GetTimeRangeFromLine(line, ref dbName).End;
            if (contentTime.End.Year < 1900)
                contentTime.End = contentTime.Begin;

            // Blocking log může obsahovat čas uložený tak debilně, že jede ve 12-ti hodinovém cyklu bez příznaku AM/PM (jak bylo nalezeno na logu z PMS).
            // Pak mi detekce času souboru selže, protože v prvním řádku najdu datum 2012-12-15 08:26:00, 
            // ale v posledním řádku najdu datum a čas 2012-12-15 01:45:00, což vypadá jako čas menší než čas na začátku.
            // Takový případ řeším přičítáním 12 hodin k času End, dokud nebude větší nebo rovný Begin:
            while (contentTime.Seconds < 0d)
                contentTime.End = contentTime.End.AddHours(12d);

            // Pokud jsem našel nějaká data, pak bych takový soubor mohl zpracovat:
            if (contentTime.Begin > DateTime.MinValue)
            {
                args.Priority = 1M;
                args.ContentTime = contentTime;
				args.TraceType = NorisBlockingConstants.TraceType;
                args.ServerTimePriority = 2;                      // Tento server má druhý nejdůvěryhodnější čas
                if (!String.IsNullOrEmpty(dbName))                //    možná ...   && String.IsNullOrEmpty(args.UserData.GetString("DbName", null)))
                    args.UserData.Set("DbName", dbName);          // Vložíme DbName načtené zevnitř trace, pokud se načetlo
                this._PrepareContentElementTexts(args);
                this._PrepareContentTreePaths(args);
            }
        }
        /// <summary>
        /// Vrátí časový rozsah daného řádku
        /// </summary>
		/// <param name="lineText"></param>
        /// <returns></returns>
		private TimeRange _GetTimeRangeFromLine(string lineText, ref string dbName)
        {
			if (lineText.IsEmpty())
				return TimeRange.Empty;

			string[] items = lineText.Split(';');
			int length = items.Length;
			if (length < 49)
				return TimeRange.Empty;

			DateTime lastTime = MainInfo.ConvertDateTime(items[17]);   // lasttime = Čas posledního výskytu blokování na daném zdroji (resource).
			int totalBlcktime;
			Int32.TryParse(items[19], out totalBlcktime);              // totalBlcktime = celkový čas blokování v milisekundách

			TimeRange timeRange = new TimeRange(lastTime.AddMilliseconds(-totalBlcktime), lastTime);

            if (dbName == null && !String.IsNullOrEmpty(items[03]))
                dbName = items[03];

			return timeRange;
        }
        /// <summary>
        /// Sestaví texty, které se budou vypisovat v elementu a v tooltipu
        /// </summary>
        /// <param name="args"></param>
        private void _PrepareContentElementTexts(TraceReaderCheckContentArgs args)
        {
            string pathDir1 = args.FileInfo.Directory.Name;         // Jméno prvního up adresáře
            string fileName = args.FileInfo.Name;
            string onlyName = Path.GetFileNameWithoutExtension(fileName);
            string fileDir1 = Path.Combine(pathDir1, fileName);     // jméno souboru s prvním adresářem a s příponou
            string fullName = args.FileInfo.FullName;
            string dbName = args.UserData.GetString("DbName", "");
            string dbNameDot = (String.IsNullOrEmpty(dbName) ? "" : dbName + ": ");

            args.ElementTexts = new string[]
            {
                dbName,
                dbNameDot  + onlyName,
                dbNameDot  + fileName,
                dbNameDot  + fileDir1,
                dbNameDot  + fullName
            };

            // Tooltip:
            args.ElementTooltip = "Vstupní soubor: " + fileDir1 + Environment.NewLine +
                "Obsahuje data za čas: " + args.ContentTime.StringShort;
        }
        /// <summary>
        /// Do argumentu připraví nody, které budeme později pravděpodobně používat:
        /// </summary>
        /// <param name="args"></param>
        private void _PrepareContentTreePaths(TraceReaderCheckContentArgs args)
        {
            // Plná cesta k nodu, absolutní (true), node = Klienti, graf = elementy se nenačítají, podřízené řádky se načítají dynamicky, třídí se podle textu:
            TreePath nodeBlock = new TreePath(true);
            nodeBlock.AddItem(CreateTreePathItemBlock());

            // Připravené dva nody dáme do TreePaths:
            args.TreePaths = new TreePath[] { nodeBlock };
        }
        /// <summary>
        /// Metoda je volaná pro zpracování celého obsahu dat.
        /// Veškerou podporu má v argumentu TraceReaderReadItemArgs args.
        /// Metoda si může libovolně zpracovat vstupující soubor 
        /// a vytvářet z něj data ve formě jednotlivých záznamů typu ITraceItem.
        /// Tyto záznamy předává ke zpracování do datového systému pomocí volání metody argumentu ProcessItem(ITraceItem item).
        /// Metoda takto může vynechat některé věty (nedávat je ke zpracování), anebo některé věty zpracovat více způsoby
        /// (například z jedné věty trace vytvoří dvě položky: jednu ktertá představuje aktivitu uživatele, a druhou která popisuje stav paměti).
        /// Tím, že zpracování probíhá v jedné metodě, si reader může udržovat kontinuální povědomí o načítaných datech, 
        /// a například o stavu paměti posílat položky při změně hodnoty (není třeba posílat 1000 x jednu hodnotu stavu paměti).
        /// </summary>
        /// <param name="args"></param>
        void ITraceReader.ProcessContent(TraceReaderProcessContentArgs args)
        {
            this._ProcessContent(args);
        }
        /// <summary>
        /// Tato metoda má za úkol vytvořit a vrátit logickou událost na základě fyzických dat načtených z trace
        /// (před malinkou chvilkou je objekt osobně načetl a vrátil v metodě ReadItem()).
        /// Pokud se vrátí null, pak tato položka trace nemá být zakládána do evidence.
        /// </summary>
        IEventItem ITraceReader.CreateIEventItem(IEventEdge data)
        {
            return _CreateIEventItem(data);
        }
        /// <summary>
        /// Metoda, která má za úkol vytvořit celou plně definovanou cestu k nodu.
        /// Tato metoda se volá zcela výjimečně - tehdy, když tento objekt předepisuje nějakou TargetPath, která dosud neexistuje.
        /// Pak si jádro vyžádá plnou cestu k nodu, včetně textů a definice všech nodů.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        void ITraceReader.CreateFullTreePath(TraceReaderCreatePathArgs args)
        {
            this.CreateFullTreePath(args);
        }
        void IDisposable.Dispose()
        {
        }
        #endregion
        #region Rozklad názvu souboru na prvky
        /// <summary>
        /// Ze jména souboru (adresář není třeba zadávat, ale nevadí) najde základ a datovou část (zde není číslo souboru).
		/// Soubor se jmenuje typicky : "d:\Data\TestThreads\Trace_Noris_ASB2_111107_threads.csv"
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="fileRoot"></param>
        /// <param name="nameDate"></param>
        internal static bool ParseFileNameV1(string fileName, out string fileRoot, out string nameDate, out string dbName)
        {
            fileRoot = null;
            nameDate = null;
            dbName = null;
            string name = System.IO.Path.GetFileNameWithoutExtension(fileName);
            int x;

            // Někde před koncem je text _blocking_, ten najdu a v jeho místě jméno rozdělím:
            string search = "_blocking_";
            int searchLen = search.Length;
            x = name.ToLower().IndexOf(search);
            if (x <= 1 || x >= (name.Length - searchLen)) return false;

            // Za textem "_blocking_" je název databáze:
            dbName = name.Substring(x + searchLen);
            name = name.Substring(0, x);

            // Před textem "_blocking_" je datum:
            x = name.LastIndexOf("_");
            if (x <= 1 || x >= name.Length) return false;
            nameDate = name.Substring(x + 1);
            fileRoot = name.Substring(0, x);

            return true;
        }
        /// <summary>
        /// Ze jména souboru (adresář není třeba zadávat, ale nevadí) najde základ a datum Begin a End.
        /// Soubor se jmenuje typicky : "d:\Data\TestThreads\BlockingLog_2013.02.08_00.00__2013.02.11_00.00.csv"
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="dateBegin"></param>
        /// <param name="dateEnd"></param>
        /// <param name="dbName"></param>
        /// <returns></returns>
        internal static bool ParseFileNameV2(string fileName, out DateTime? dateBegin, out DateTime? dateEnd, out string dbName)
        {
            dateBegin = null;
            dateEnd = null;
            dbName = null;
            if (String.IsNullOrEmpty(fileName)) return false;
            string name = System.IO.Path.GetFileNameWithoutExtension(fileName);

            // na začátku je text "BlockingLog_", ten odeberu:
            string format = "BlockingLog_2013.02.08_00.00__2013.02.11_00.00";
            //               0123456789012345678901234567890123456789012345
            //               0         1         2         3         4     
            string token = "BlockingLog_";
            if (name.Length < format.Length || !name.StartsWith(token, StringComparison.OrdinalIgnoreCase)) return false;

            // následují datum a čas začátku a konce ve formátu "2013.02.08_00.00__2013.02.11_00.00" :
            dateBegin = ParseFileDate(name.Substring(12, 16));
            dateEnd = ParseFileDate(name.Substring(30, 16));
            return (dateBegin.HasValue && dateEnd.HasValue);
        }
        /// <summary>
        /// Vrátí datum ze stringu, který má být ve formátu yyyy.MM.dd_HH.mm
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        internal static DateTime? ParseFileDate(string text)
        {
            System.Globalization.DateTimeFormatInfo dfi = DTFormatV2;
            DateTime dateTime;
            if (DateTime.TryParseExact(text, "F", dfi, System.Globalization.DateTimeStyles.AllowWhiteSpaces | System.Globalization.DateTimeStyles.NoCurrentDateDefault, out dateTime))
                return dateTime;
            return null;
        }
        protected static System.Globalization.DateTimeFormatInfo DTFormatV2
        {
            get
            {
                if (_DTFormatV2 == null)
                {
                    System.Globalization.DateTimeFormatInfo dfi = new System.Globalization.DateTimeFormatInfo();
                    dfi.FullDateTimePattern = "yyyy.MM.dd_HH.mm";
                    _DTFormatV2 = dfi;
                }
                return _DTFormatV2;
            }
        }
        private static System.Globalization.DateTimeFormatInfo _DTFormatV2;
        /// <summary>
        /// Z textu s datem v názvu souboru (text ve formě "YYMMDD") určí a vrátí datum trace.
        /// Pokud nejde určit, vrací MinValue.
        /// </summary>
        /// <param name="nameDate"></param>
        /// <returns></returns>
        internal static DateTime DetectNameDate(string nameDate)
        {
            DateTime result = DateTime.MinValue;
            if (!String.IsNullOrEmpty(nameDate) && nameDate.Length == 6)
            {
                int year, month, day;
                if (Int32.TryParse(nameDate.Substring(0, 2), out year) &&
                    Int32.TryParse(nameDate.Substring(2, 2), out month) &&
                    Int32.TryParse(nameDate.Substring(4, 2), out day))
                {
                    if (month > 0 && month <= 12 &&
                        day > 0 && day <= 31)
                    {
                        try
                        {
                            result = new DateTime(2000 + year, month, day);
                        }
                        catch
                        {
                            result = DateTime.MinValue;
                        }
                    }
                }
            }
            return result;
        }
        #endregion
		#region Fyzické načítání obsahu souboru a zpracování na položky ITraceItem
		/// <summary>
		/// Zpracuje data do jednotlivých položek
		/// </summary>
		/// <param name="args"></param>
		private void _ProcessContent(TraceReaderProcessContentArgs args)
		{
			TraceServerPersistData persistData = TraceServerPersistData.GetFrom(args.Server.Tag);
			try
			{
				this.StringHeap = args.Main.StringHeap;
				this.TitleLine = args.FileStreamReader.ReadLine();
				this.LineNumber = 0;

                // Předáme datum procesu z trace souboru do položek (tam se uvádí jen Time, ne Date):
                persistData.TraceFileDate = args.TraceDate;

                // Nastavím režim ošetření duplicity EventItem na SilentSkip:
                // Co to znamená: pokud zpracovávám log blokování z více aplikačních serverů (pooly), které jsou generovány pro jednu a tu samou databázi, pak se data v těch souborech opakují.
                //   Log blokování je totiž opis tabulky blokování z databáze. A více aplikačních serverů jej vytvoří vícekrát, se stejným obsahem.
                // Konstrukcí klíčů (Server, Process) je zajištěno, že z více trace souborů pro jednu databázi se založí právě jen jeden proces.
                // A řízením duplicity (SilentSkip) se zajistí, že duplicitní eventy se nebudou zobrazovat vícekrát, jen jednou (poprvé).
                args.Server.DupliciteEventKeyHandling = DupliciteEventKeyHandlingType.SilentSkip;

				while (!args.FileStreamReader.EndOfStream)
				{
					string lineText = args.FileStreamReader.ReadLine();
					this.LineNumber++;

                    // Data v trace typu Log blokování jsou vždy ucelené objekty typu EventItem, zde není třeba načítat je nejprve do TraceItem a z něj teprve vytvářet do EventItem.
                    // (Určení: TraceItem typicky odpovídá jedné hraně události (Begin, End), a teprve ze dvou takových událostí se skládá EventItem).

                    // Z řádku tedy rovnou vytvořím EventItem a předám jej do zpracování.
                    // Zatřídění události: všechny logy blokování vkládají své eventy do serveru [NorisBlockingConstants.ServerName].
                    // V rámci tohoto serveru existují dynamicky zakládané procesy, jejichž Key = EventItem.DbName
                    // V rámci každého procesu existují eventy, jejichž Key = EventItem.Id.
                    EventItem eventItem = EventItem.CreateItem(args.Main.StringHeap, args.Trace, (this.LineNumber + 1), lineText, ref persistData);
                    if (eventItem != null)
                    {
                        args.ProcessEvent(eventItem);
                    }
				}
			}
			finally
			{
				args.Server.Tag = persistData;
				this.StringHeap = null;
			}
		}
		/// <summary>
		/// Reference na zásobník textů
		/// </summary>
		protected StringHeap StringHeap { get; set; }
		protected string TitleLine { get; set; }
		protected int LineNumber { get; set; }
		#endregion
		#region Podpora pro tvorbu nodů grafu (řádky: klíče, názvy, definice grafiky a chování řádku)
		/// <summary>
		/// Sestavení plné cesty k nodu na základě jejího stringu
		/// </summary>
		/// <param name="args"></param>
		protected void CreateFullTreePath(TraceReaderCreatePathArgs args)
		{
			// Připravím si polotovar (tam se mi rozeberou jednotlivé složky cesty):
			TreePath input = new TreePath(args.Path);
			List<TreePathItem> items = input.Items;

			// Připravím si výsledek:
			TreePath result = new TreePath(input.AbsolutePath);
			string status = (input.AbsolutePath ? "root" : "node");           // Jakou část cesty budu nyní zpracovávat? Dynamicky se posouvá podle toho, jak se posouvám na konkrétní cestě
			foreach (TreePathItem item in items)
			{
				switch (status)
				{
					case "root":
						// Řeším node v root úrovni:
						// Podle klíče položky určím, co v ní je, a jakého typu bude příští položka:
						switch (item.Key)
						{
							case NorisBlockingConstants.NodeBlocking:
								result.AddItem(TraceReader.CreateTreePathItemBlock());
								status = "dbname";    // V příští úrovni cesty (další TreePathItem item in items) očekávám název databáze
								break;
						}
						break;
					case "dbname":
						// V této úrovni očekávám název databáze:
						result.AddItem(TraceReader.CreateTreePathItemBlockDbName(args, item));
						status = "objname";           // V příští úrovni cesty bude jméno objektu
						break;
					case "objname":
						// V této úrovni bude login uživatele + profil:
                        result.AddItem(TraceReader.CreateTreePathItemBlockObject(args, item));
						status = "detail";                     // V příští úrovni cesty budou detailní položky (???)
						break;
					default:
						result.AddItem(TraceReader.CreateTreePathItemDefault(item.Key));
						break;
				}
			}
			args.TreePath = result;
		}
		/// <summary>
        /// Vrátí definici pro položku grafu s textem "Blokování", která se zobrazuje v root úrovni grafu.
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemBlock()
		{
			return new TreePathItem(NorisBlockingConstants.NodeBlocking, "Blokování",
				GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.DynamicSubNodeLoading | RowProperties.ChildSortedText));
		}
		/// <summary>
        /// Vrátí definici pro položku grafu s textem "Databáze", která se zobrazuje jako podřízená položka k root nodu Blokování.
		/// </summary>
		/// <returns></returns>
		private static TreePathItem CreateTreePathItemBlockDbName(TraceReaderCreatePathArgs args, TreePathItem item)
		{
            string dbName = item.Key;                 // Název objektu
			return new TreePathItem(item.Key, dbName,
				GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.ChildSortedKey));
		}
		/// <summary>
        /// Vrátí definici pro položku grafu s textem "Objekt blokování", která se zobrazuje jako podřízená položka k root nodu Databáze.
		/// </summary>
		/// <returns></returns>
        private static TreePathItem CreateTreePathItemBlockObject(TraceReaderCreatePathArgs args, TreePathItem item)
		{
            string objName = item.Key;                // Název objektu = objekt ["." index]
			objName = objName.Replace(".", Environment.NewLine);
			return new TreePathItem(item.Key, objName,
				GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.DynamicElementsLoading));
		}
		/// <summary>
		/// Vrátí definici pro položku grafu defaultní, bez podrobnější znalost věci
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemDefault(string key)
		{
			string name = key;
			return new TreePathItem(key, name,
				GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.PreLoadingElements | RowProperties.DynamicElementsLoading | RowProperties.DynamicSubNodeLoading));
		}
		#endregion
		#region Vytvoření logické události IEventItem na základě dat z trace IEventEdge
		/// <summary>
		/// Tato metoda má za úkol vytvořit a vrátit logickou událost na základě fyzických dat načtených z trace
		/// (před malinkou chvilkou je objekt osobně načetl a vrátil v metodě ReadItem()).
		/// Pokud se vrátí null, pak tato položka trace nemá být zakládána do evidence.
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		private IEventItem _CreateIEventItem(IEventEdge data)
		{
            throw new InvalidOperationException("TraceReader typu DbBlocking nemá vytvářet objekty IEventItem pomocí metody CreateIEventItem()!");
			return null;
		}
		#endregion
	}
	/// <summary>
	/// Instance této třídy persistuje v ServerInfo.Tag mezi jednotlivými soubory Trace
	/// (tedy mezi jednotlivými voláními metody this._ProcessContent).
	/// Tím je umožněno předávat si kontinuální data do načítání dalšího souboru.
	/// </summary>
	internal class TraceServerPersistData
	{
		internal static TraceServerPersistData GetFrom(object tag)
		{
			TraceServerPersistData data = null;
			if (tag != null && tag is TraceServerPersistData)
				data = tag as TraceServerPersistData;
			if (data == null)
				data = new TraceServerPersistData();
			return data;
		}
		/// <summary>
		/// Datum souboru
		/// </summary>
		internal DateTime TraceFileDate { get; set; }
        /// <summary>
        /// Blokovaná databáze, určeno ze jména souboru
        /// </summary>
        internal DateTime TraceDbName { get; set; }
    }
	#endregion
	#region Konstanty NorisBlockingConstants
	/// <summary>
	/// Konstanty používané v datech trace blokování
	/// </summary>
	public class NorisBlockingConstants
	{
		/// <summary>
        /// NorisDbServerBlocking (druh dat v trace souboru)
		/// </summary>
        public const string TraceType = "NorisDbServerBlocking";
		/// <summary>
        /// 50_Blocking
		/// </summary>
		internal const string NodeBlocking = "50_Blocking";
		/// <summary>
        /// \50_Blocking
		/// </summary>
		internal const string RootNodeBlocking = "\\" + NodeBlocking;
        /// <summary>
        /// GreenSqlBlocking = název logického serveru v TraceData
        /// </summary>
        internal const string ServerName = "GreenSqlBlocking";
	}
    /// <summary>
    /// Formát blocking souboru
    /// </summary>
    public enum FileFormat
    {
        None = 0,
        OnlineSave,
        ExportFunction
    }
	#endregion
}
